﻿#include "cbaslergigecamera.h"
#include <QDebug>
#include <QThread>

namespace Gen {

//CImageEventHandler
REG_CAMERA_DRIVER("GigeBasler", CBaslerGigeCameraDriver)

enum MyEvents
{
    eMyExposureEndEvent  = 100,
    eMyEventOverrunEvent = 200
};
///////////////////////////////////////////////////////////////////////////////////////
//! \brief The CBaslerGigeCameraDriver class
///////////////////////////////////////////////////////////////////////////////////////
CBaslerGigeCameraDriver::CBaslerGigeCameraDriver()
{
    Pylon::PylonInitialize();
    Pylon::CTlFactory& tlFactory = Pylon::CTlFactory::GetInstance();

    int devicesNum = tlFactory.EnumerateDevices(m_devList);

    for (int i = 0; i < devicesNum; i++)
    {
        QString deviceName = m_devList[i].GetUserDefinedName();

        if (m_devList[i].GetDeviceClass() == "BaslerGigE")
        {
            m_nameList.append(deviceName);
        }
        else
        {
            m_nameList.append(NULL);
        }
    }
}

QStringList CBaslerGigeCameraDriver::cameraNameList()const
{
    return m_nameList;
}

ICamera* CBaslerGigeCameraDriver::cameraInstance(const QString& name)
{
    qDebug() << "name gige2: " << m_nameList;
    if (!m_nameList.contains(name))return 0;
    int index = m_nameList.indexOf(name);
    return new CBaslerGigeCamera(m_devList[index]);
}


///////////////////////////////////////////////////////////////////////////////////////
//! \brief The CBaslerGigeCamera class
///////////////////////////////////////////////////////////////////////////////////////
CBaslerGigeCamera::CBaslerGigeCamera(const Pylon::CDeviceInfo& deviceInfo):
    m_deviceInfo(deviceInfo)
{

    m_camera = new Camera_t(Pylon::CTlFactory::GetInstance().CreateDevice(deviceInfo));
    m_camera->GrabCameraEvents = true;
    open();

}

CBaslerGigeCamera::~CBaslerGigeCamera()
{
    m_camera->DestroyDevice();
    delete m_camera;
}

bool CBaslerGigeCamera::open()
{
    bool status;
    m_camera->MaxNumBuffer = 5;
    m_camera->Open();
    m_nodeMap = &m_camera->GetNodeMap();
    status = m_camera->IsOpen();


    // Check if the device supports events.
    if ( !GenApi::IsAvailable( m_camera->EventSelector))
    {
        throw RUNTIME_EXCEPTION( "The device doesn't support events.");
    }

    // Enable sending of Exposure End events.
    // Select the event to receive.
    m_camera->EventSelector.SetValue(Basler_GigECameraParams::EventSelector_ExposureEnd);
    m_camera->EventNotification.SetValue(Basler_GigECameraParams::EventNotification_GenICamEvent);

    // Enable it.
    m_camera->RegisterImageEventHandler(this, Pylon::RegistrationMode_Append, Pylon::Ownership_ExternalOwnership);
    m_camera->RegisterCameraEventHandler( this, "ExposureEndEventData", eMyExposureEndEvent, Pylon::RegistrationMode_ReplaceAll, Pylon::Cleanup_None);

    if (status) emit opened();

    return status;
}

bool CBaslerGigeCamera::isOpened() const
{
    return m_camera->IsOpen();
}

void CBaslerGigeCamera::close()
{
    if (!m_camera->IsOpen())return;
    m_camera->Close();
    emit closed();
}

void CBaslerGigeCamera::destroyDevice()
{
    m_camera->DestroyDevice();
}

int CBaslerGigeCamera::gain() const
{
    if (!m_camera->IsOpen())return 0;
    try
    {
        GenApi::CIntegerPtr node = m_nodeMap->GetNode("GainRaw");
        return node->GetValue();
    }
    catch (GenICam::GenericException& e)
    {
        qDebug() << e.GetDescription();
        return -1;
    }
}

void CBaslerGigeCamera::setGain(int value)
{
    if (!m_camera->IsOpen())return;
    try
    {
        GenApi::CIntegerPtr node = m_nodeMap->GetNode("GainRaw");
        if (value >= node->GetMin() && value <= node->GetMax())
            node->SetValue(value);
        else if (value > node->GetMax())
            node->SetValue(node->GetMax());
        else
            node->SetValue(node->GetMin());
        emit gainChanged();
    }
    catch (GenICam::GenericException& e)
    {
        qDebug() << e.GetDescription();
        return ;
    }
}

int CBaslerGigeCamera::maxGain() const
{
    if (!m_camera->IsOpen())return 0;
    try
    {
        GenApi::CIntegerPtr node = m_nodeMap->GetNode("GainRaw");
        return node->GetMax();
    }
    catch (GenICam::GenericException&)
    {
        return -1;
    }
}

int CBaslerGigeCamera::minGain() const
{
    if (!m_camera->IsOpen())return 0;
    try
    {
        GenApi::CIntegerPtr node = m_nodeMap->GetNode("GainRaw");
        return node->GetMin();
    }
    catch (GenICam::GenericException&)
    {
        return -1;
    }
}

int CBaslerGigeCamera::exposure() const
{
    if (!m_camera->IsOpen())return 0;
    try
    {
        GenApi::CIntegerPtr node = m_nodeMap->GetNode ("ExposureTimeRaw");
        return node->GetValue();
    }
    catch (GenICam::GenericException& e)
    {
        qDebug() << e.GetDescription();
        return -1;
    }
}

void CBaslerGigeCamera::setExposure(int value)
{
    if (!m_camera->IsOpen())return;
    try
    {
        GenApi::CIntegerPtr node = m_nodeMap->GetNode("ExposureTimeRaw");
        int n = node->GetInc();
        value = value / n * n;
        if (value >= node->GetMin() && value <= node->GetMax())
            node->SetValue(value);
        else if (value > node->GetMax())
            node->SetValue(node->GetMax());
        else node->SetValue(node->GetMin());
        emit exposureChanged();
    }
    catch (GenICam::GenericException& e)
    {
        qDebug() << e.GetDescription();
        return;
    }
}

int CBaslerGigeCamera::maxExposure() const
{
    if (!m_camera->IsOpen())return 0;
    try
    {
        GenApi::CIntegerPtr node = m_nodeMap->GetNode ("ExposureTimeRaw");
        return node->GetMax();
    }
    catch (GenICam::GenericException& e)
    {
        qDebug() << e.GetDescription();
        return -1;
    }
}

int CBaslerGigeCamera::minExposure() const
{
    if (!m_camera->IsOpen())return 0;
    try
    {
        GenApi::CIntegerPtr node = m_nodeMap->GetNode ("ExposureTimeRaw");
        return node->GetMin();
    }
    catch (GenICam::GenericException& e)
    {
        qDebug() << e.GetDescription();
        return -1;
    }
}

bool CBaslerGigeCamera::setFrameRate(float value)
{
    if (!m_camera->IsOpen())return 0;
    try
    {
        GenApi::CFloatPtr node = m_nodeMap->GetNode("AcquisitionFrameRateAbs");
        GenApi::CBooleanPtr frame = m_nodeMap->GetNode("AcquisitionFrameRateEnable");
        frame->SetValue(1);
        if (value >= node->GetMin() && value <= node->GetMax())
        {
            node->SetValue(value);
        }
        else if (value > node->GetMax())
        {
            node->SetValue(node->GetMax());
        }
        else
        {
            node->SetValue(node->GetMin());
        }
        emit frameRateChanged();
    }
    catch (GenICam::GenericException& e)
    {
        qDebug() << e.GetDescription();
        return 0;
    }
    return true;
}

ICamera::TriggerMode CBaslerGigeCamera::triggerMode() const
{
    if (!m_camera->IsOpen())return Software;
    GenApi::CEnumerationPtr  node  = m_nodeMap->GetNode ("TriggerMode");
    if (node->GetIntValue()) return TriggerMode::Hardware;
    else return TriggerMode::Software;
}

void CBaslerGigeCamera::setTriggerMode(ICamera::TriggerMode mode)
{
    if (!m_camera->IsOpen())return;
    GenApi::CEnumerationPtr  triggermode  = m_nodeMap->GetNode ("TriggerMode");
    GenApi::CEnumerationPtr triggersource = m_nodeMap->GetNode("TriggerSource");
    ICamera::TriggerMode curtrigmode = triggerMode();

    if (curtrigmode == mode)
        return;

    triggermode->SetIntValue(1);             //Trigger Mode  硬触发
    if (mode == Hardware)//设置触发边沿
    {
        GenApi::CEnumerationPtr  triggerActivation  = m_nodeMap->GetNode ("TriggerActivation");
        triggerActivation->SetIntValue(1);
    }
    else if (mode == Software)
    {
        triggersource->SetIntValue(Basler_GigECamera::TriggerSourceEnums::TriggerSource_Software);
    }


    emit triggerModeChanged();
}

bool CBaslerGigeCamera::setCameraEvent()
{
    if ( !GenApi::IsAvailable( m_camera->EventSelector))
    {
        return false;
        throw RUNTIME_EXCEPTION( "The device doesn't support events.");
    }
    // Enable sending of Exposure End events.
    // Select the event to receive.
    m_camera->EventSelector.SetValue(Basler_GigECameraParams::EventSelector_ExposureEnd);
    // Enable it.
    // m_camera->EventNotification.SetValue(Basler_GigECameraParams::EventNotification_GenICamEvent);
    return true;
}

bool CBaslerGigeCamera::blockGrabOne(int timeout)
{
    if (!m_camera->IsOpen())return false;
    if (m_camera->IsGrabbing())
    {
        qWarning() << "You must stop grab continue first!!!";
        return false;
    }
    Pylon::CGrabResultPtr ptrGrabResult;
    clear();
    if (m_camera->IsPylonDeviceAttached())
    {
        m_camera->GrabOne(timeout, ptrGrabResult);
        return true;
    }
    return false;
}

bool CBaslerGigeCamera::grabOne()
{
    if (m_camera->CanWaitForFrameTriggerReady())
    {
        if (!m_camera->IsGrabbing())
            m_camera->StartGrabbing(Pylon::GrabStrategy_OneByOne, Pylon::GrabLoop_ProvidedByInstantCamera);
        if ( m_camera->WaitForFrameTriggerReady( 500, Pylon::TimeoutHandling_ThrowException))
        {
            m_camera->ExecuteSoftwareTrigger();
            return true;
        }
        else
            return false;
    }
    else
        return false;
}

bool CBaslerGigeCamera::grabContinue()
{
    if (!m_camera->IsOpen())return false;
    if (m_camera->IsPylonDeviceAttached())
    {
        if (m_camera->IsGrabbing())return false;
        m_camera->StartGrabbing(Pylon::GrabStrategy_LatestImages, Pylon::GrabLoop_ProvidedByInstantCamera);
        emit contiuneChanged();
        return true;

    }
    return false;
}

bool CBaslerGigeCamera::grabStop()
{
    if (!m_camera->IsOpen())return false;
    if (!m_camera->IsPylonDeviceAttached())return false;
    if (!m_camera->IsGrabbing())return false;
    m_camera->StopGrabbing();
    emit contiuneChanged();
    return true;

}

bool CBaslerGigeCamera::isContinue() const
{
    if (!m_camera->IsOpen())return false;
    if (!m_camera->IsPylonDeviceAttached())return false;
    return m_camera->IsGrabbing();
}

void CBaslerGigeCamera::OnImageGrabbed(Pylon::CInstantCamera& camera, const Pylon::CGrabResultPtr& result)
{
    Q_UNUSED(camera)
    if (result->GrabSucceeded())
    {
        m_width = result->GetWidth();
        m_height = result->GetHeight();
        if (m_buffer.size() != m_width * m_height)m_buffer.resize(m_width * m_height);
        memcpy(m_buffer.data(), result->GetBuffer(), m_width * m_height);
        m_ready = true;
        emit ready();
    }
    else
    {
        m_grabErr = true;
        qWarning() << "Grab image error!!!";
    }
}

QImage CBaslerGigeCamera::image() const
{
    return QImage(m_buffer.data(), m_width, m_height, m_imgFormat);
}

}
